//
//  MetalViewController.swift
//  ZSceneDemo
//
//  Created by 张鑫 on 2020/9/15.
//  Copyright © 2020 www.lionaitech.com. All rights reserved.
//

import UIKit

import Metal
import MetalKit

class MetalViewController: UIViewController {
    
    var device: MTLDevice?
    var queue: MTLCommandQueue?
    var pipeline: MTLRenderPipelineState?
    var renderDescriptor: MTLRenderPassDescriptor?
    var mtkRect: CGRect?
    
    var mtkview: MTKView?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        
        device = MTLCreateSystemDefaultDevice()
        if device == nil {
            print("do not support metal")
            return
        }
        print("support metal name is \(String(describing: device?.name))")
        // 414, 696
        mtkRect = view.bounds
        mtkRect?.origin.y = 100
        mtkRect?.size.height -= 200
        mtkview = MTKView(frame: mtkRect ?? .zero, device: device)
        view.addSubview(mtkview!)
        
        
        queue = device?.makeCommandQueue()
        
        createPipeline()
        createRenderPassDescriptor()
        renderCommandEncoder()
        
    }
    
    /**
     与metal文件中的定义一一对应就可以，不一定需要使用 bridge 文件
     使用 bridge 文件，有利于定义的统一，防止认为错误
     struct VertexIn {
     var position: vector_float4
     var color: vector_float3
     }
     */
    func getVertexBuffer() -> MTLBuffer? {
        // 创建顶点 [[buffer(0)]] 参数， 需要跟encoder 中的index对应
        let vertexArrayData: [VertexIn] = [
            // 前 4 位 位置 x , y , z ,w
            VertexIn(position: vector_float4(-100,  100, 0.0, 1.0), color: vector_float3(1,0,0), texture: vector_float2(0,0)),
            VertexIn(position: vector_float4( 100,  100, 0.0, 1.0), color: vector_float3(1,0,1), texture: vector_float2(1,0)),
            VertexIn(position: vector_float4(-100, -100, 0.0, 1.0), color: vector_float3(1,1,0), texture: vector_float2(0,1)),
            VertexIn(position: vector_float4( 100, -100, 0.0, 1.0), color: vector_float3(1,1,0), texture: vector_float2(1,1)),
        ]
        
        let size = MemoryLayout<VertexIn>.size * vertexArrayData.count
        
        let data: UnsafeMutableRawPointer = malloc(size)
        data.initializeMemory(as: VertexIn.self, from: vertexArrayData, count: vertexArrayData.count)
        
        let vertexBuffer = device?.makeBuffer(bytes: data, length: size, options: .storageModeShared)
        
        return vertexBuffer
    }
    
    func getScaleBuffer() -> MTLBuffer? {
        // 创建显示区域范围参数 [[buffer(1)]] 参数， 需要跟encoder 中的index对应
        let width: uint = uint(mtkRect?.size.width ?? 0)
        let height: uint = uint(mtkRect?.size.height ?? 0)
        
        var ss = Viewport(scale: Float(UIScreen.main.scale), viewportSize: vector_uint2(width, height))
        
        let size = MemoryLayout<VertexIn>.size
        
        let vertexBuffer = device?.makeBuffer(bytes: &ss, length: size, options: .storageModeShared)
        
        return vertexBuffer
    }
    
    func getTexture() -> MTLTexture? {
        guard let d = device else {
            return nil
        }
        
        guard let image = UIImage(named: "art.scnassets/texture.png")?.cgImage else {
            print("image load failed")
            return nil
        }
        // 使用纹理加载器，把图片生成纹理
        let loader = MTKTextureLoader(device: d)
        let texture = try? loader.newTexture(cgImage: image, options: nil)
        print(texture?.sampleCount ?? -1)
        return texture
    }
    
    func createPipeline() {
        // 提取MSL中的函数名称
        guard let library = device?.makeDefaultLibrary() else {
            print("create shader library failed")
            return
        }
        print(library.functionNames)
        
        guard let vertexFunction = library.makeFunction(name: "myVertexShader") else {
            print("get vertex function failed")
            return
        }
        guard let fragmentFunction = library.makeFunction(name: "myFragmentShader") else {
            print("get fragment function failed")
            return
        }
        
        // 创建管道，绑定MSL中的顶点函数 和 面函数
        let descriptor = MTLRenderPipelineDescriptor()
        descriptor.label = "pipeline descriptor"
        descriptor.vertexFunction = vertexFunction
        descriptor.fragmentFunction = fragmentFunction
        descriptor.colorAttachments[0].pixelFormat = mtkview?.colorPixelFormat ?? .bgra8Unorm
        //        descriptor.depthAttachmentPixelFormat = .depth32Float
        
        pipeline = try? device?.makeRenderPipelineState(descriptor: descriptor)
    }
    
    func createRenderPassDescriptor() {
        
        renderDescriptor = MTLRenderPassDescriptor()
        
        renderDescriptor?.colorAttachments[0].loadAction = .clear
        renderDescriptor?.colorAttachments[0].storeAction = .store
        // 绘制区域的背景色
        renderDescriptor?.colorAttachments[0].clearColor = MTLClearColor(red: 0.5, green: 0.65, blue: 0.8, alpha: 1)
        
        /**
         设置效果显示范围，默认整个mtkview
         设置以像素为单位设置
         与pipeline state 的 descriptor 的depthAttachmentPixelFormat 同时设置
         */
        // let textureDescriptor = MTLTextureDescriptor()
        // textureDescriptor.width = Int((mtkRect?.size.width ?? 0) * UIScreen.main.scale)
        // textureDescriptor.height = Int((mtkRect?.size.height ?? 0) * UIScreen.main.scale)
        // textureDescriptor.pixelFormat = .depth32Float
        // textureDescriptor.storageMode = .private
        // textureDescriptor.usage = .renderTarget
        //
        // renderDescriptor?.depthAttachment.texture = device?.makeTexture(descriptor: textureDescriptor)
    }
    
    func renderCommandEncoder() {
        //ios 13可用
        // guard let drawable = (mtkview?.layer as? CAMetalLayer)?.nextDrawable() else {
        guard let drawable = mtkview?.currentDrawable else {
            print("drawable is nil")
            return
        }
        
        let commandBuffer = queue?.makeCommandBuffer()
        
        renderDescriptor?.colorAttachments[0].texture = drawable.texture
        
        let samplerDescriptor = MTLSamplerDescriptor()
        samplerDescriptor.magFilter = .linear
        samplerDescriptor.minFilter = .linear
        // sampler 从gpu中创建，也可以在Metal文件中自行创建
        let sampler = device?.makeSamplerState(descriptor: samplerDescriptor)
        
        
        // 创建渲染指令
        let encoder = commandBuffer?.makeRenderCommandEncoder(descriptor: renderDescriptor!)
        encoder?.setCullMode(.none)
        encoder?.setFrontFacing(.counterClockwise)
        //        encoder?.setViewport(MTLViewport(originX: 0, originY: 0, width: 0, height: 0, znear: 0, zfar: 0))
        
        //绑定 顶点数据、纹理数据和管道
        encoder?.setRenderPipelineState(pipeline!)
        encoder?.setVertexBuffer(getVertexBuffer(), offset: 0, index: 0)
        encoder?.setVertexBuffer(getScaleBuffer(), offset: 0, index: 1)
        encoder?.setFragmentTexture(getTexture(), index: 0)
        encoder?.setFragmentSamplerState(sampler, index: 0)
        
        
        encoder?.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4)
        encoder?.endEncoding()
        
        
        // 提交数据给GPU 进行绘制
        commandBuffer?.present(drawable)
        commandBuffer?.commit()
        
    }
    
    /**
     创建Metal资源，分为两种
     1. MTLBuffer 代表着未格式化的内存，可以是任何类型的数据。 Buffer 用来做顶点着色和计算状态。
     2. MTLTexture 代表着有着特殊纹理类型和像素格式的格式化的图像数据。用来做顶点，面和计算的源
     
     
     */
    
    
    
    
}
